package org.spring.blog.service.impl;

import org.spring.blog.dao.LRImageDao;
import org.spring.blog.pojo.LRImage;
import org.spring.blog.pojo.LRPageList;
import org.spring.blog.pojo.User;
import org.spring.blog.response.ResponseResult;
import org.spring.blog.response.ResponseState;
import org.spring.blog.service.IImageService;
import org.spring.blog.service.IUserService;
import org.spring.blog.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
@Transactional
public class ImageService implements IImageService {
    // 应为 SimpleDateFormat 线程不安全，需要设置一个线程锁
    // 或者不重用 每次都 new 一个新对象

    @Value("${sob.blog.image.save-path}")
    public String imagePath;

    @Value("${sob.blog.image.max-size}")
    public long imageMaxSize;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    LRImageDao lrImageDao;

    @Autowired
    UserService userService;


    /**
     * 返回二维码的方法
     *
     * @param _code     二维码对应的id
     * @param _response 响应体
     * @param _request  请求体
     */
    @Override
    public void getQrCodeImage(String _code, HttpServletResponse _response, HttpServletRequest _request) {
        // check data
        // 检查二维码是否过期
        String loginState = (String) redisUtil.get(IUserService.redis_pc_scan_login_key + _code);
        if (TextUtil.isEmpty(loginState)) {
            // TODO: 返回一张图片，二维码已经过期
            return;
        }

        // complete data
        // 切割出域名+ 端口
        String originalDomain = _request.getRequestURL().toString().replace(_request.getServletPath(), "");

        // 二维码内容 app 下载地址 + 判断登录状态 flag 的 id
        String content = originalDomain + app_download_path + "===" + _code;

        byte[] bytes = LRQrCodeUtil.encodeQRCode(content);

        // complete return data
        _response.setContentType(LRQrCodeUtil.RESPONSE_CONTENT_TYPE);

        try {
            ServletOutputStream outputStream = _response.getOutputStream();
            assert bytes != null;
            outputStream.write(bytes);
            outputStream.flush();
        } catch (IOException _e) {
            _e.printStackTrace();
        }
    }

    /**
     * 上传图片
     *
     * @param _file     文件路径
     * @param _original 图片来源
     * @return 自定义返回对象
     */
    @Override
    public ResponseResult uploadImage(MultipartFile _file, String _original) throws IOException {
        // check data
        switch (_original) {
            case db_image_original_friend_link:
            case db_image_original_loop:
            case db_image_original_user_avatar:
            case db_image_original_article:
            case db_image_original_article_cover:
                break;
            default:
                return ResponseResult.failed("未定义的来源");
        }
        if (_file == null) {
            return ResponseResult.failed(ResponseState.file_cant_empty_message, ResponseState.file_cant_empty_code);
        }

        String imageType = _file.getContentType();


        if (TextUtil.isEmpty(imageType)) {
            return ResponseResult.failed(ResponseState.file_type_error_message, ResponseState.file_type_error_code);
        }

        if (_file.getSize() > imageMaxSize) {
            return ResponseResult.failed(ResponseState.file_size_too_big_message, ResponseState.file_size_too_big_code);
        }


        for (int i = 0; i < constant.support_image_type.length; i++) {

            if (constant.support_image_type[i].equals(imageType)) {

                String[] split = imageType.split("/");

                imageType = split[1];
                break;
            } else if (i == constant.support_image_type.length - 1) {
                return ResponseResult.failed(ResponseState.file_type_no_support_message, ResponseState.file_type_no_support_code);
            }
        }

        // complete data

        // 获取当前时间
        String currentDay = new SimpleDateFormat("yyyy_MM_dd").format(new Date());

        // 生成保存路径
        // 使用一个id作为图片名称
        long nextId = snowflakeIdWorker.nextId();

        File targetFile = new File(imagePath +
                File.separator + currentDay +
                File.separator + imageType +
                File.separator + nextId + "." + imageType);


        if (!targetFile.getParentFile().exists()) {
            if (!targetFile.getParentFile().mkdirs()) {
                return ResponseResult.failed();
            }
        }

        if (!targetFile.exists()) {
            if (!targetFile.createNewFile()) {
                return ResponseResult.failed();
            }
        }

        // complete data for mysql database
        String resultPath = System.currentTimeMillis() + "_" + nextId + "." + imageType;

        LRImage lrImage = new LRImage();
        lrImage.setContentType(_file.getContentType())
                .setId(String.valueOf(snowflakeIdWorker.nextId()))
                .setCreateTime(new Date())
                .setUpdateTime(new Date())
                .setPath(targetFile.getPath())
                .setName(_file.getOriginalFilename())
                .setUrl(resultPath)
                .setUserId(userService.checkUserIsLogin().getId())
                .setState(LRConstants.db_publish_status)
                .setOriginal(_original);

//        System.out.println(targetFile);

        // save data
        try {
            _file.transferTo(targetFile);
        } catch (IOException _e) {
            _e.printStackTrace();
            return ResponseResult.failed();
        }

        lrImageDao.save(lrImage);


        // complete return data
        // 返回图片的 访问路径 和 图片名称
        Map<String, String> result = new HashMap<>();


        result.put("path", resultPath);
        result.put("name", _file.getOriginalFilename());

        // return data
        return ResponseResult.success().setData(result);
    }

    /**
     * 查找图片
     *
     * @param _url      图片id
     * @param _response 响应体
     */
    @Override
    public void query(String _url, HttpServletResponse _response) throws IOException {
        // check data

        // complete data
        String[] paths = _url.split("_");

        // 一个时间戳
        // attention: 使用时间戳字符串 需要转成long 类型
        long dayValue = Long.parseLong(paths[0]);
        String pictureName = paths[1];

        // java 使用 点[.]分割字符需要转义字符
        String[] split = pictureName.split("\\.");

        String type = split[1];

        // 创建完整图片路径 root + day + type + 123.jpg
        //
        File file = new File(imagePath +
                File.separator + new SimpleDateFormat("yyyy_MM_dd").format(dayValue) +
                File.separator + type +
                File.separator + pictureName);

        _response.setContentType("image/jpeg");

        ServletOutputStream servletOutputStream = _response.getOutputStream();

        FileInputStream fileInputStream = new FileInputStream(file);

        //noinspection CheckForOutOfMemoryOnLargeArrayAllocation
        byte[] bytes = new byte[1024];

        int len;
        while ((len = fileInputStream.read(bytes)) != -1) {
            servletOutputStream.write(bytes, 0, len);t
        }


        // change data
        servletOutputStream.flush();

        fileInputStream.close();
        servletOutputStream.close();
    }

    /**
     * 获取图片列表
     *
     * @param _page     页码
     * @param _size     页面大小
     * @param _original 图片来源
     * @return 自定义返回对象
     */
    @Override
    public ResponseResult list(int _page, int _size, String _original) {
        // check data
        if (_page < constant.list_default_page_num) {
            _page = constant.list_default_page_num;
        }

        if (_size < constant.list_min_page_size) {
            _size = constant.list_min_page_size;
        }

        User userIsLogin = userService.checkUserIsLogin();

        if (userIsLogin == null) {
            return ResponseResult.failed(ResponseState.user_not_login_message, ResponseState.user_not_login_code);
        }

        // complete data from mysql database
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");

        Pageable pageable = PageRequest.of(_page - 1, _size, sort);

        Page<LRImage> all = lrImageDao.findAll(new Specification<LRImage>() {
            private static final long serialVersionUID = -3502280724904171606L;

            @Override
            public Predicate toPredicate(Root<LRImage> _root, CriteriaQuery<?> _criteriaQuery, CriteriaBuilder _criteriaBuilder) {
                Predicate state = _criteriaBuilder.equal(_root.get("state").as(String.class), LRConstants.db_publish_status);
                Predicate userId = _criteriaBuilder.equal(_root.get("userId").as(String.class), userIsLogin.getId());

                Predicate original = null;

                if (TextUtil.notEmpty(_original)) {
                    if (_original.equals(db_image_original_all)) {
                        original = _criteriaBuilder.like(_root.get("original").as(String.class), "%");
                    } else {
                        original = _criteriaBuilder.equal(_root.get("original").as(String.class), _original);
                    }
                }

                return _criteriaBuilder.and(userId, state, original);
            }
        }, pageable);

        LRPageList<LRImage> result = new LRPageList<>(all.getContent(), _page, all.getTotalElements(), all.getSize());


        // complete data
        // return data
        return ResponseResult.success().setData(result);
    }

    /**
     * 删除图片
     *
     * @param _id 图片id
     * @return 自定义返回对象
     */
    @Override
    public ResponseResult delete(String _id) {

        int i = lrImageDao.deleteByState(_id, LRConstants.db_ban_status);

        if (i <= 0) {
            return ResponseResult.failed(ResponseState.file_no_have_message, ResponseState.file_no_have_code);
        } else {
            return ResponseResult.success();
        }
    }
}
